import os
import multiprocessing
import multiprocessing.connection
import platform
import time
import socket
import errno

import made.util.misc

_was_shut_down = False
def shut_down_daemon():
    global _was_shut_down
    _was_shut_down = True

def _connection_address():
    return (r"\\.\pipe\madeserver_pipe"
        if platform.system() == "Windows"
        else "/tmp/madeserver_socket")

def start_daemon(process_request_callback):
    if platform.system() != "Windows":
        made.util.misc.ensure_containing_dir_exists(_connection_address())

    if os.path.exists(_connection_address()):
        os.unlink(_connection_address())

    listening_connection = multiprocessing.connection.Listener(
        _connection_address())

    while not _was_shut_down:
        connection = _accept_connection(listening_connection)
        _read_and_process_request(connection, process_request_callback)

def _accept_connection(listening_connection):
    """Accept a connection, retrying if the system call returned early because
    it handled a signal.  Retrying is useful when running the server in debug
    mode, allowing a KeyboardInterrupt to start the debugger.
    """
    while True:
        try:
            return listening_connection.accept()
        except socket.error, e:
            if e.errno != errno.EINTR:
                raise

def wait_for_and_open_connection():
    for timeout in (0.1, 0.1, 0.1, 0.1, 1, 2, 3):
        time.sleep(timeout)
        connection = open_connection()
        if connection is not None:
            return connection

    return None

def _read_and_process_request(connection, process_request_callback):
    request = read_from_connection(connection)
    response = process_request_callback(request)
    send_over_connection(connection, response)
    connection.close()

def open_connection():
    no_connection_exception = (
        WindowsError if platform.system() == "Windows" else socket.error)
    try:
        return multiprocessing.connection.Client(_connection_address())
    except no_connection_exception:
        return None

def send_over_connection(connection, dictionary):
    connection.send(dictionary)

def read_from_connection(connection):
    return connection.recv()

def close_connection(connection):
    connection.close()

